package cn.ec.utils;

/**
 * 将下划线命名转换成驼峰式命名
 * 例:user_id -> userId
 * ASCII表
 * Dec  Char                           Dec  Char     Dec  Char     Dec  Char
 * ---------                           ---------     ---------     ----------
 * 0  NUL (null)                      32  SPACE     64  @         96  `
 * 1  SOH (start of heading)          33  !         65  A         97  a
 * 2  STX (start of text)             34  "         66  B         98  b
 * 3  ETX (end of text)               35  #         67  C         99  c
 * 4  EOT (end of transmission)       36  $         68  D        100  d
 * 5  ENQ (enquiry)                   37  %         69  E        101  e
 * 6  ACK (acknowledge)               38  &         70  F        102  f
 * 7  BEL (bell)                      39  '         71  G        103  g
 * 8  BS  (backspace)                 40  (         72  H        104  h
 * 9  TAB (horizontal tab)            41  )         73  I        105  i
 * 10  LF  (NL line feed, new line)    42  *         74  J        106  j
 * 11  VT  (vertical tab)              43  +         75  K        107  k
 * 12  FF  (NP form feed, new page)    44  ,         76  L        108  l
 * 13  CR  (carriage return)           45  -         77  M        109  m
 * 14  SO  (shift out)                 46  .         78  N        110  n
 * 15  SI  (shift in)                  47  /         79  O        111  o
 * 16  DLE (data link escape)          48  0         80  P        112  p
 * 17  DC1 (device control 1)          49  1         81  Q        113  q
 * 18  DC2 (device control 2)          50  2         82  R        114  r
 * 19  DC3 (device control 3)          51  3         83  S        115  s
 * 20  DC4 (device control 4)          52  4         84  T        116  t
 * 21  NAK (negative acknowledge)      53  5         85  U        117  u
 * 22  SYN (synchronous idle)          54  6         86  V        118  v
 * 23  ETB (end of trans. block)       55  7         87  W        119  w
 * 24  CAN (cancel)                    56  8         88  X        120  x
 * 25  EM  (end of medium)             57  9         89  Y        121  y
 * 26  SUB (substitute)                58  :         90  Z        122  z
 * 27  ESC (escape)                    59  ;         91  [        123  {
 * 28  FS  (file separator)            60  <         92  \        124  |
 * 29  GS  (group separator)           61  =         93  ]        125  }
 * 30  RS  (record separator)          62  >         94  ^        126  ~
 * 31  US  (unit separator)            63  ?         95  _        127  DEL
 */
public class CamelUtil {

    /**
     * 下划线对应的ASCII
     */
    private static final byte ASCII_UNDER_LINE = 95;

    /**
     * 小写字母a的ASCII
     */
    private static final byte ASCII_a = 97;

    /**
     * 大写字母A的ASCII
     */
    private static final byte ASCII_A = 65;

    /**
     * 小写字母z的ASCII
     */
    private static final byte ASCII_z = 122;

    /**
     * 字母a和A的ASCII差距(a-A的值)
     */
    private static final byte ASCII_a_A = ASCII_a - ASCII_A;

    /**
     * 将参数b转换为大写字母,小写字母ASCII范围(97~122)
     * 0. 判断参数是否为小写字母
     * 1. 将小写字母转换为大写字母(减去32)
     */
    private static byte toUpper(byte b) {
        if (b >= ASCII_a && b <= ASCII_z) {
            return (byte) (b - ASCII_a_A);
        }
        return b;
    }

    /**
     * 交换下划线和其后面字符的下标
     * 将column从下划线命名方式转换成驼峰命名方式
     * 0. 找到`_`符号的ASCII码(95)对应的下标
     * 1. 将下划线的下标的下一个元素转换为大写字段(如果是小写字母的话)并放到下划线对应的下标
     * 2. 将下划线下标的下一个元素设置为下划线
     * 3. 返回数组
     *
     * @param column 字段名称
     */
    private static byte[] changeIdx(String column) {
        byte[] bytes = column.getBytes();
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] == ASCII_UNDER_LINE) {
                if (i < bytes.length - 1) {
                    bytes[i] = toUpper(bytes[i + 1]);
                    bytes[i + 1] = ASCII_UNDER_LINE;
                    i++;
                }
            }
        }
        return bytes;
    }

    /**
     * 去除所有下划线
     * 0. 新创建一个数组
     * 1. 将所有非下划线字符都放入新数组中
     *
     * @param bytes 原始数组
     * @return 处理后的字节数组
     */
    private static byte[] removeUnderLine(byte[] bytes) {
        // 存放非下划线字符的数量
        int count = 0;
        for (byte b : bytes) {
            if (b == ASCII_UNDER_LINE) {
                continue;
            }
            count++;
        }
        byte[] nBytes = new byte[count];
        count = 0;
        for (byte b : bytes) {
            if (b == ASCII_UNDER_LINE) {
                continue;
            }
            nBytes[count] = b;
            count++;
        }
        return nBytes;
    }

    /**
     * 将下划线命名转换驼峰式命名
     * 0. 转换成大写,交换下标
     * 1. 去除所有下划线
     *
     * @param column 原始字段
     * @return 新字段
     */
    public static String toCamel(String column) {
        byte[] bytes = changeIdx(column);
        bytes = removeUnderLine(bytes);
        return new String(bytes);
    }

    public static void main(String[] args) {
        System.out.println(toCamel("user_id"));//打印结果: userId
        System.out.println(toCamel("hello_world_to_lucky_man"));//打印结果: helloWorldToLuckyMan
    }

}
